package com.aliware.tianchi.loadbalance;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author lorne
 */
public class ProviderStatus<T> {

    private final static Map<String, String> MAX_THREADS = new ConcurrentHashMap<>();

    private final static Map<String, ProviderStatus> SERVICE_STATISTICS = new ConcurrentHashMap<String, ProviderStatus>();

    public static <T> ProviderStatus providerStatus(Invoker<T> invoker, Invocation invocation) {
        URL url = invoker.getUrl();
        String key = url.toIdentityString() + "#" + invocation.getMethodName();
        ProviderStatus providerStatus = SERVICE_STATISTICS.get(key);
        if (providerStatus == null) {
            SERVICE_STATISTICS.putIfAbsent(key, new ProviderStatus(url.getAddress(), invoker));
            providerStatus = SERVICE_STATISTICS.get(key);
        }
        if (providerStatus.getMaxThreads() == null) {
            providerStatus.setMaxThreads(MAX_THREADS.get(url.getAddress()));
        }
        providerStatus.setInvoker(invoker);
        return providerStatus;
    }

    private String address;

    private String maxThreads;

    private final AtomicInteger active = new AtomicInteger();

    private BaseScore baseScore;

    private final AtomicInteger total = new AtomicInteger();

    private final AtomicInteger failed = new AtomicInteger();

    private int threads;

    private Invoker<T> invoker;

    private WeightNode weightNode = new WeightNode();

    public void setInvoker(Invoker<T> invoker) {
        this.invoker = invoker;
    }

    public WeightNode initWeightNode() {
        if(failed.get()>10) {
            weightNode.init(getNowAvg(), getConcurrency(), invoker);
        }else{
            weightNode.init(getNowAvg(), getThreads(), invoker);
        }
        return weightNode;
    }

    private ProviderStatus(String address, Invoker<T> invoker) {
        this.invoker = invoker;
        this.address = address;
    }


    public static void pushMaxThreads(String key, String threads) {
        MAX_THREADS.put(key, threads);
    }

    public String getMaxThreads() {
        return maxThreads;
    }

    public void setMaxThreads(String maxThreads) {
        if (!StringUtils.isEmpty(maxThreads)) {
            this.maxThreads = maxThreads;
            threads = Integer.parseInt(maxThreads);
            baseScore = new BaseScore(threads);
        }
    }

    public int getThreads() {
        return threads;
    }

    public long getActive() {
        return active.get();
    }

    public double getNowAvg() {
        return baseScore.getNowAvg();
    }

    public double getConcurrency() {
        return baseScore.getConcurrency();
    }

    public boolean beginCount(String activeStr) {
        int max;
        try {
            max = Integer.parseInt(activeStr);
        } catch (Exception e) {
            max = Integer.MAX_VALUE;
        }
        long val = active.incrementAndGet();
        if (val > max) {
            active.decrementAndGet();
            return false;
        } else {
            return true;
        }
    }

    public void beginCount() {
        active.incrementAndGet();
    }

    public void endCount(long elapsed, boolean success) {
        active.decrementAndGet();
        total.incrementAndGet();
        if(!success){
            failed.incrementAndGet();
        }
        if (maxThreads != null&&success) {
            baseScore.analysis(elapsed);
        }
    }


    public String getAddress() {
        return address;
    }
}
